﻿/*************************************************************************************
 * 工 具 名:   HK_FreeCam
 * 
 * 描    述:   这是一个用于Unity引擎的自由摄像机测试脚本（其实没什么实际作用），可以通过键盘和鼠标实现3D视角的自由移动和旋转。
 *            此脚本工具适用于在开发和调试期间对场景中的摄像机视角进行灵活控制，进行场景游览。
 *            开发测试结束后可直接将该组件移除。
 * 
 * 使用方式:   1.将 HK_FreeCam.cs 脚本文件添加到 Unity 项目中的任一 GameObject 上，通常是添加到主摄像机上。
 *            2.调整脚本中暴露的参数，如 moveSpeed 表示移动速度，rotateSpeed 表示旋转速度，以及 xAxisRange 表示摄像机俯仰角度限制。
 *            3.按住WASD键进行前后左右移动，QE键进行上下移动；
 *            4.按住Shift进行位移加速；
 *            5.按住鼠标右键并移动鼠标左、右、上、下旋转摄像机视角；
 *            6.滚动鼠标滚轮，视野缩放；
 * 
 * 版    本：  V1.1
 * 创 建 者：  京产肠饭
 * 创建时间：  2021/8/25
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using UnityEngine;

namespace HKTools
{
    public class HK_FreeCam : MonoBehaviour
    {
        [SerializeField, Header("摄像机默认移动速度：")]
        float moveSpeed = 10;
        float curMoveSpeed; // 摄像机当前移动速度

        [SerializeField, Header("摄像机默认旋转速度：")]
        float rotateSpeed = 300f;

        [SerializeField, Header("摄像机俯仰限制范围：")]
        Vector2 xAxisRange = new Vector2(-45, 45);

        Transform mainCamera_TF;

        bool isQuicken; // 是否加速

        Vector3 camTargetPos; // 摄像机目标位置
        Vector3 camTargetEuler; // 摄像机目标欧拉角
        float camTargetFOV; // 摄像机目标FOV

        void Start()
        {
            mainCamera_TF = Camera.main.transform; // 获取主摄像机的Transform组件
            camTargetPos = mainCamera_TF.position; // 初始化摄像机目标位置为当前摄像机位置
            camTargetEuler = mainCamera_TF.localEulerAngles; // 初始化摄像机目标欧拉角为当前摄像机欧拉角
            camTargetFOV = Camera.main.fieldOfView; // 初始化摄像机目标FOV为当前FOV

            isQuicken = false;  // 初始化加速状态为否
            curMoveSpeed = moveSpeed;   // 初始化当前移动速度为默认移动速度
        }

        void LateUpdate()
        {
            ControlCam();   // 控制摄像机移动和旋转
            AddSpeed();     // 控制加速
            ZoomCamera();   // 控制摄像机缩放
            LerpCameraTransform();  // 平滑插值更新摄像机的位置、旋转和FOV
        }

        void OnGUI()
        {
            GUIStyle fontStyle = new GUIStyle();    // 创建GUI样式

            fontStyle.normal.background = null; // 设置背景填充
            fontStyle.normal.textColor = new Color(0.1f, 0.9f, 0); // 设置字体颜色为绿色
            fontStyle.fontSize = 20; // 设置字体大小
            fontStyle.fontStyle = FontStyle.Bold;

            GUILayout.Label("1、按住鼠标右键，旋转视角", fontStyle);
            GUILayout.Label("2、滚动鼠标滚轮，缩放视角", fontStyle);
            GUILayout.Label("3、按住W、A、S、D键，前后左右移动", fontStyle);
            GUILayout.Label("4、按住Q、E键，上下移动", fontStyle);
            GUILayout.Label("5、按住Shift键，加速", fontStyle);

            GUILayout.Label("6、测试结束后，请手动移除该脚本", fontStyle);
        }

        /// <summary>
        /// 控制摄像机的3D视角移动和旋转
        /// </summary>
        void ControlCam()
        {
            // 按 W 键前进
            if (Input.GetKey(KeyCode.W))
                camTargetPos = camTargetPos + mainCamera_TF.forward * curMoveSpeed * Time.deltaTime;

            // 按 S 键后退
            if (Input.GetKey(KeyCode.S))
                camTargetPos = camTargetPos - mainCamera_TF.forward * curMoveSpeed * Time.deltaTime;

            // 按 A 键向左
            if (Input.GetKey(KeyCode.A))
                camTargetPos = camTargetPos - mainCamera_TF.right * curMoveSpeed * Time.deltaTime;

            // 按 D 键向右
            if (Input.GetKey(KeyCode.D))
                camTargetPos = camTargetPos + mainCamera_TF.right * curMoveSpeed * Time.deltaTime;

            // 按 Q 键上升
            if (Input.GetKey(KeyCode.Q))
                camTargetPos = camTargetPos + Vector3.up * curMoveSpeed * Time.deltaTime;

            // 按 E 键下降
            if (Input.GetKey(KeyCode.E))
                camTargetPos = camTargetPos + Vector3.down * curMoveSpeed * Time.deltaTime;

            // 按住鼠标右键，旋转视角
            if (Input.GetMouseButton(1))
            {
                camTargetEuler = camTargetEuler + Vector3.up * Input.GetAxisRaw("Mouse X") * rotateSpeed * Time.deltaTime;
                camTargetEuler = camTargetEuler + Vector3.left * Input.GetAxisRaw("Mouse Y") * rotateSpeed * Time.deltaTime;

                // 限制摄像机的俯仰角度
                camTargetEuler.x = Mathf.Clamp(camTargetEuler.x, xAxisRange.x, xAxisRange.y);

                // 隐藏鼠标指针
                Cursor.visible = false;
            }

            // 松开鼠标右键时显示鼠标指针
            if (Input.GetMouseButtonUp(1))
                Cursor.visible = true;
        }

        /// <summary>
        /// 控制摄像机加速
        /// </summary>
        void AddSpeed()
        {
            // 按下 Shift 键，加速
            if (Input.GetKeyDown(KeyCode.LeftShift))
            {
                curMoveSpeed = moveSpeed * 3;
                isQuicken = true;
            }

            // 松开 Shift 键，停止加速
            if (Input.GetKeyUp(KeyCode.LeftShift))
            {
                isQuicken = false;
            }

            // 松开 Shift 键后，逐渐减速
            if (!isQuicken && curMoveSpeed > moveSpeed)
            {
                curMoveSpeed = Mathf.Max(moveSpeed, curMoveSpeed - 100 * Time.deltaTime);
            }
        }

        /// <summary>
        /// 控制摄像机缩放
        /// </summary>
        void ZoomCamera()
        {
            // 滚动鼠标滚轮，缩小视野
            if (Input.GetAxis("Mouse ScrollWheel") > 0)
            {
                camTargetFOV = Mathf.Max(40, camTargetFOV - 80 * Time.deltaTime);
            }
            // 滚动鼠标滚轮，放大视野
            if (Input.GetAxis("Mouse ScrollWheel") < 0)
            {
                camTargetFOV = Mathf.Min(80, camTargetFOV + 80 * Time.deltaTime);
            }
        }

        /// <summary>
        /// 平滑插值更新摄像机的位置、角度和FOV
        /// </summary>
        void LerpCameraTransform()
        {
            // 平滑插值更新摄像机FOV
            if (Mathf.Abs(Camera.main.fieldOfView - camTargetFOV) > 0.01f)
            {
                Camera.main.fieldOfView = Mathf.Lerp(Camera.main.fieldOfView, camTargetFOV, Time.deltaTime * 10);
            }

            // 平滑插值更新摄像机位置
            if (Vector3.Distance(mainCamera_TF.position, camTargetPos) > 0.1f)
            {
                mainCamera_TF.position = Vector3.Lerp(mainCamera_TF.position, camTargetPos, Time.deltaTime * 10);
            }

            // 平滑插值更新摄像机角度
            if (Quaternion.Dot(mainCamera_TF.rotation, Quaternion.Euler(camTargetEuler)) != 1)
            {
                mainCamera_TF.rotation = Quaternion.Lerp(mainCamera_TF.rotation, Quaternion.Euler(camTargetEuler), Time.deltaTime * 10);
            }
        }
    }
}